using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;

namespace SymAutomate.JobFile
{
    /// <summary>
    /// Handles Jobfile functions.
    /// Error Numbers 350-399; Reference file ErrorNumbers.txt for more information.
    /// 350 Error: Jobfile %JobFileName% not found.
    /// 351 Error: Timeout waiting for prompt %prompt%. Verify if the 'jobfileparam' are consistent with the Jobfile.
    /// 352 Error: Jobfile prompt inconsistent. Got stuck on %error%.
    /// 353 Error: Timeout waiting for Jobfile %JobFileName% to complete. Try increasing the value for 'jobfileruntimeoutinsec' or run in Async mode.
    /// 354 Error: Error/Timeout displaying/getting batch queue current status.
    /// 355 Error: Could not calculate first available queue.
    /// 356 Error: JobFileScheduleDate is invalid or not in proper format. Value format is mm/dd/yy.
    /// 357 Error: JobFileScheduleTime is invalid or not in proper format. It should be a number between 0000 and 2359.
    /// 358 Error: Scheduled Start Date and Time is past.
    /// 359 Error: Logged Off Due to Inactivity. Console's 'Automatic Logoff Time' must be greater than or equal to 'jobfileruntimeoutinsec'.
    /// 360 Error: JobFileExpectedSystemDate is invalid or not in proper format. Value format is mm/dd/yy.
    /// 361 Error: JobFileExpectedPreviousSystemDate is invalid or not in proper format. Value format is mm/dd/yy.
    /// </summary>
    class Driver : DriverBase, DriverInterface
    {
        private Parameters __Parameters;

        public Driver()
            : base()
        {
        }

        public Driver(ConsoleArguments _ConsoleArguments, HostTelnetConnection.Driver _HostTelnetConnectionDriver)
            : base(_HostTelnetConnectionDriver)
        {
            __Parameters = new Parameters(_ConsoleArguments, base.ConsoleHelpList);
        }

        /// <summary>
        /// Defines the command to function mapping;
        /// First parameter is what to expect as "command" from commmand line;
        /// Second parameter is the function name (case must match);
        /// </summary>
        public void PopulateCommandToFunctionMapping()
        {
            CommandToFunctionMapping.Add("RunJobFile".ToLower(), "RunJobFile");
        }

        /// <summary>
        /// Defines the help text for this driver
        /// </summary>
        public void PopulateConsoleHelp()
        {
            ConsoleHelp _ConsoleHelp = new ConsoleHelp("RunJobFile");
            _ConsoleHelp.Parameters.Add("JobFileName");
            _ConsoleHelp.Parameters.Add("JobFileRunTimeoutInSec");
            _ConsoleHelp.Parameters.Add("JobFileBatchQueue");
            _ConsoleHelp.Parameters.Add("JobFileRunAsync");
            _ConsoleHelp.Parameters.Add("JobFileParam");
            _ConsoleHelp.Parameters.Add("JobFileScheduleDate");
            _ConsoleHelp.Parameters.Add("JobFileScheduleTime");
            _ConsoleHelp.Parameters.Add("JobFileExpectedSystemDate");
            _ConsoleHelp.Parameters.Add("JobFileExpectedPreviousSystemDate");
            ConsoleHelpList.Add(_ConsoleHelp);
        }
        
        /// <summary>
        /// Runds a Jobfile
        /// </summary>
        public void RunJobFile()
        {
            string s = "";

            int _BatchQueueToRunJob = __Parameters.JobFileBatchQueue;

            try
            {
                HostTelnetConnectionDriver.Login();

                HostTelnetConnectionDriver.TelnetSend("8" + GlobalParameters.TelnetSendEndOfCommand); //Management Menu
                s = HostTelnetConnectionDriver.TelnetWaitFor("Menu Selection");
                HostTelnetConnectionDriver.LogIncommingStream(s);

                HostTelnetConnectionDriver.TelnetSend("0" + GlobalParameters.TelnetSendEndOfCommand); //Batch Control
                s = HostTelnetConnectionDriver.TelnetWaitFor("Selection");
                HostTelnetConnectionDriver.LogIncommingStream(s);

                HostTelnetConnectionDriver.TelnetSend("0" + GlobalParameters.TelnetSendEndOfCommand); //Run a Jobfile
                s = HostTelnetConnectionDriver.TelnetWaitFor("Job File Name");
                HostTelnetConnectionDriver.LogIncommingStream(s);

                HostTelnetConnectionDriver.TelnetSend(__Parameters.JobFileName + GlobalParameters.TelnetSendEndOfCommand); //Specify Jobfile Name
                try
                {
                    s = HostTelnetConnectionDriver.TelnetWaitFor("Batch Options?");
                }
                catch (Dart.Telnet.DataException _DataException)
                {
                    HostTelnetConnectionDriver.LogIncommingStream(_DataException.DataRead.ToString());
                    throw new ReturnException("350 Error: Jobfile '" + __Parameters.JobFileName + "' not found.");
                }
                HostTelnetConnectionDriver.LogIncommingStream(s);

                HostTelnetConnectionDriver.TelnetSend("Y" + GlobalParameters.TelnetSendEndOfCommand); //Answer Y to 'Batch Options?'
                s = HostTelnetConnectionDriver.TelnetWaitFor("Display Batch Queues");
                HostTelnetConnectionDriver.LogIncommingStream(s);

                if (__Parameters.JobFileBatchQueue != 99) //batch queue to run this job is already specified
                {
                    HostTelnetConnectionDriver.TelnetSend("N" + GlobalParameters.TelnetSendEndOfCommand); //Answer N to 'Display Batch Queues'
                    s = HostTelnetConnectionDriver.TelnetWaitFor("Notify Upon Completion");
                    HostTelnetConnectionDriver.LogIncommingStream(s);
                }
                else //we need to find the first avaiable batch queue
                {
                    HostTelnetConnectionDriver.TelnetSend("Y" + GlobalParameters.TelnetSendEndOfCommand); //Answer Y to 'Display Batch Queues'

                    int _LocalLoopCount = 0;
                    string _BatchQueueText = string.Empty;
                    bool _BatchQueueDisplayEnded = false;
                    while (!_BatchQueueDisplayEnded && _LocalLoopCount < 99)
                    {
                        try
                        {
                            s = HostTelnetConnectionDriver.TelnetWaitFor("Notify Upon Completion");
                            HostTelnetConnectionDriver.LogIncommingStream(s);
                            _BatchQueueDisplayEnded = true;
                            _BatchQueueText += s;
                        }
                        catch (Dart.Telnet.DataException _DataException)
                        {
                            _BatchQueueText += HostTelnetConnectionDriver.LogIncommingStream(_DataException.DataRead.ToString());
                            HostTelnetConnectionDriver.TelnetSend("" + GlobalParameters.TelnetSendEndOfCommand); //Send [Enter]
                            _LocalLoopCount++;
                        }
                    }
                    if (!_BatchQueueDisplayEnded)
                    {
                        throw new ReturnException("354 Error: Error/Timeout displaying/getting batch queue current status.");
                    }

                    _BatchQueueToRunJob = this.GetFirstAvailableQueue(HostTelnetConnectionDriver.RemoveNonPrintableCharacters(_BatchQueueText));
                }

                HostTelnetConnectionDriver.TelnetSend("Y" + GlobalParameters.TelnetSendEndOfCommand); //Answer Y to 'Notify Upon Completion'
                s = HostTelnetConnectionDriver.TelnetWaitFor("Queue Priority");
                HostTelnetConnectionDriver.LogIncommingStream(s);

                HostTelnetConnectionDriver.TelnetSend("1" + GlobalParameters.TelnetSendEndOfCommand); //Answer 1 to 'Queue Priority
                s = HostTelnetConnectionDriver.TelnetWaitFor("Scheduled Start Date");
                HostTelnetConnectionDriver.LogIncommingStream(s);

                HostTelnetConnectionDriver.TelnetSend(__Parameters.JobFileScheduleDate + GlobalParameters.TelnetSendEndOfCommand); //Answer to 'Scheduled Start Date'
                s = HostTelnetConnectionDriver.TelnetWaitFor(new string[] { "Scheduled Start Time", "Scheduled Start Date" });
                s = HostTelnetConnectionDriver.LogIncommingStream(s);

                if (s.Contains("Scheduled Start Date")) throw new ReturnException("356 Error: JobFileScheduleDate is invalid or not in proper format. Value format is mm/dd/yy.");

                HostTelnetConnectionDriver.TelnetSend(__Parameters.JobFileScheduleTime + GlobalParameters.TelnetSendEndOfCommand); //Answer to 'Scheduled Start Time'
                s = HostTelnetConnectionDriver.TelnetWaitFor(new string[] { "Batch Queue", "Scheduled Start Time", "Scheduled Start Date  [" });
                s = HostTelnetConnectionDriver.LogIncommingStream(s);

                if (s.Contains("Scheduled Start Time") || s.Contains("Scheduled Start Date  ["))
                {
                    //if the last message contains "Scheduled Start Date and Time is past" then throw error 358
                    if (s.Contains("Scheduled Start Date and Time is Past"))
                        throw new ReturnException("358 Error: Scheduled Start Date and Time is past.");
                    throw new ReturnException("357 Error: JobFileScheduleTime is invalid or not in proper format. It should be a number between 0000 and 2359.");
                }

                HostTelnetConnectionDriver.TelnetSend(_BatchQueueToRunJob + GlobalParameters.TelnetSendEndOfCommand); //Answer to 'Batch Queue'

                //if the job is being scheduled for future
                if (__Parameters.JobFileScheduleDate != string.Empty || __Parameters.JobFileScheduleTime != string.Empty)
                {
                    s = HostTelnetConnectionDriver.TelnetWaitFor("Expected System Date ");
                    HostTelnetConnectionDriver.LogIncommingStream(s);

                    HostTelnetConnectionDriver.TelnetSend(__Parameters.JobFileExpectedSystemDate + GlobalParameters.TelnetSendEndOfCommand); //Answer to 'Expected System Date'
                    s = HostTelnetConnectionDriver.TelnetWaitFor(new string[] { "Expected Previous System Date", "Expected System Date " });
                    s = HostTelnetConnectionDriver.LogIncommingStream(s);

                    if (s.Contains("Expected System Date ")) throw new ReturnException("360 Error: JobFileExpectedSystemDate is invalid or not in proper format. Value format is mm/dd/yy.");

                    HostTelnetConnectionDriver.TelnetSend(__Parameters.JobFileExpectedPreviousSystemDate + GlobalParameters.TelnetSendEndOfCommand); //Answer to 'Expected Previous System Date'
                    try
                    {
                        s = HostTelnetConnectionDriver.TelnetWaitFor("Expected Previous System Date");
                        //if above is found then Expected Previous Date is invalid
                        HostTelnetConnectionDriver.LogIncommingStream(s);
                        throw new ReturnException("361 Error: JobFileExpectedPreviousSystemDate is invalid or not in proper format. Value format is mm/dd/yy.");
                    }
                    catch (Dart.Telnet.DataException _DataException)
                    {
                        //ignore everything is good; read the buffer since try thew an excpetion and we don't have anything in variable 's'
                        s = HostTelnetConnectionDriver.LogIncommingStream(_DataException.DataRead.ToString());
                    }
                }

                //Parameter Arguments may need to be supplied
                for (int i = 0; i < __Parameters.JobFileParameter.JobFileParameterSegmentCollection.Count; i++)
                {
                    JobFileParameter.JobFileParameterSegment _JobFileParameterSegment = __Parameters.JobFileParameter.JobFileParameterSegmentCollection[i];
                    try
                    {
                        //if we are scheduling this job
                        if ((__Parameters.JobFileScheduleDate != string.Empty || __Parameters.JobFileScheduleTime != string.Empty) && i == 0)
                        {
                            if (!s.Contains(_JobFileParameterSegment.Prompt))
                                throw new Exception("Prompt not found!");
                        }
                        else
                        {
                            s = HostTelnetConnectionDriver.TelnetWaitFor(_JobFileParameterSegment.Prompt);
                            HostTelnetConnectionDriver.LogIncommingStream(s);
                        }
                    }
                    catch (Exception _JobFileParameterException)
                    {
                        throw new ReturnException("351 Error: Timeout waiting for prompt '" + _JobFileParameterSegment.Prompt + "'. Verify if the 'jobfileparam' are consistent with the Jobfile.");
                    }  
                    HostTelnetConnectionDriver.TelnetSend(_JobFileParameterSegment.Value + GlobalParameters.TelnetSendEndOfCommand);
                }

                string _GotStuckPrompt = string.Empty;
                try
                {
                    //if we are scheduling this job and no extra parameters arguments have been specified
                    if ((__Parameters.JobFileScheduleDate != string.Empty || __Parameters.JobFileScheduleTime != string.Empty) &&
                        __Parameters.JobFileParameter.JobFileParameterSegmentCollection.Count == 0)
                    {
                        if (!s.Contains("Okay?"))
                        {
                            _GotStuckPrompt = s;
                            throw new Exception("Prompt not found!");
                        }
                    }
                    else
                    {
                        s = HostTelnetConnectionDriver.TelnetWaitFor("Okay?");
                        HostTelnetConnectionDriver.LogIncommingStream(s);
                    }
                }
                catch (Exception _JobFileParameterException)
                {
                    if (_JobFileParameterException is Dart.Telnet.DataException)
                        s = HostTelnetConnectionDriver.LogIncommingStream(((Dart.Telnet.DataException)_JobFileParameterException).DataRead.ToString());
                    else
                        s = HostTelnetConnectionDriver.LogIncommingStream(string.Empty);
                    _GotStuckPrompt = _GotStuckPrompt == string.Empty ? s : _GotStuckPrompt;
                    throw new ReturnException("352 Error: Jobfile prompt inconsistent. Got stuck on " + Environment.NewLine + HostTelnetConnectionDriver.RemoveNonPrintableCharacters(_GotStuckPrompt) + ".");
                }
                

                //If running jobfile synchronously
                if (!__Parameters.JobFileRunAsync && __Parameters.JobFileScheduleDate == string.Empty && __Parameters.JobFileScheduleTime == string.Empty)
                {
                    //Change the timeout before we start running the jobfile
                    HostTelnetConnectionDriver.TelnetChangeTimeout(__Parameters.JobFileRunTimeoutInSec);

                    HostTelnetConnectionDriver.TelnetSend("Y" + GlobalParameters.TelnetSendEndOfCommand); //Answer Y to 'Okay?'
                    try
                    {
                        s = HostTelnetConnectionDriver.TelnetWaitFor("Batch Job " + __Parameters.JobFileName + " Is Done!");
                        HostTelnetConnectionDriver.LogIncommingStream(s);
                    }
                    catch (Dart.Telnet.DataException _DataException)
                    {
                        s = HostTelnetConnectionDriver.LogIncommingStream(_DataException.DataRead.ToString());
                        if (s.Contains("Logged Off Due to Inactivity"))
                            throw new ReturnException("359 Error: Logged Off Due to Inactivity. Console's 'Automatic Logoff Time' must be greater than or equal to 'jobfileruntimeoutinsec'.");
                        throw new ReturnException("353 Error: Timeout waiting for Jobfile '" + __Parameters.JobFileName + "' to complete. Try increasing the value for 'jobfileruntimeoutinsec' or run in Async mode.");
                    }

                    HostTelnetConnectionDriver.TelnetChangeTimeout(HostTelnetConnectionDriver.Parameters.TelnetCommandsTimeoutInSec);
                }
                else //running aysnc or scheduled
                {
                    HostTelnetConnectionDriver.TelnetSend("Y" + GlobalParameters.TelnetSendEndOfCommand); //Answer Y to 'Okay?'
                    s = HostTelnetConnectionDriver.TelnetWaitFor("Batch Control Menu");
                    HostTelnetConnectionDriver.LogIncommingStream(s);
                    System.Threading.Thread.Sleep(HostTelnetConnectionDriver.Parameters.TelnetCommandsTimeoutInSec * 1000);
                }
            }
            catch (Exception e)
            {
                if (e is Dart.Telnet.DataException)
                    s = HostTelnetConnectionDriver.LogIncommingStream(((Dart.Telnet.DataException)e).DataRead.ToString());
                else
                    s = HostTelnetConnectionDriver.LogIncommingStream(string.Empty);
                HostTelnetConnectionDriver.HandleException(e, s);
            }
            finally
            {
                HostTelnetConnectionDriver.TelnetCloseConnection();
            }
        }

        /// <summary>
        /// Calculates the first available queue from the _BatchQueueText
        /// </summary>
        /// <param name="_BatchQueueText"></param>
        /// <returns></returns>
        private int GetFirstAvailableQueue(string _BatchQueueText)
        {
            try
            {
                List<bool> _QueueFlagList = new List<bool>();
                _QueueFlagList.Add(false); //0
                _QueueFlagList.Add(false); //1
                _QueueFlagList.Add(false); //2
                _QueueFlagList.Add(false); //3

                MatchCollection _MatchCollection;
                Regex _Regex = new Regex(@"(\d+)(\s+)(\d+)(.+?)(Running)(.+)", RegexOptions.IgnoreCase);
                _MatchCollection = _Regex.Matches(_BatchQueueText);
                for (int i = 0; i < _MatchCollection.Count; i++)
                {
                    _QueueFlagList[Convert.ToInt32(_MatchCollection[i].Groups[3].Value)] = true;
                }

                for (int i = 0; i < _QueueFlagList.Count; i++)
                {
                    if (_QueueFlagList[i] == false)
                        return i;
                }

                //if all the queues are busy; then return 0
                return 0;
            }
            catch (Exception e1)
            {
                throw new ReturnException("355 Error: Could not calculate first available queue. " + e1.Message);
            }
        }
    }
}
